#include <asm/pgtable.h>
#include <asm/uaccess.h>
-#if 1
+#if 0
#define ASSERT(_p) \
if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
__LINE__, __FILE__); *(int*)0=0; }
#define DPRINTK(_f, _a...) ((void)0)
#endif
-static char *fixup_buf;
+static unsigned char *fixup_buf;
#define FIXUP_BUF_USER PAGE_SIZE
#define FIXUP_BUF_ORDER 1
#define FIXUP_BUF_SIZE (PAGE_SIZE<<FIXUP_BUF_ORDER)
#define PATCH_LEN 5
struct fixup_entry {
- unsigned long patch_addr; /* XXX */
unsigned char patched_code[20];
unsigned short patched_code_len;
unsigned short fixup_idx;
/* Helpful codes for the main decode routine. */
#define CODE_MASK (3<<6)
-#define PUSH (0<<6) /* PUSH onto stack */
-#define POP (1<<6) /* POP from stack */
-#define JMP (2<<6) /* 8-bit relative JMP */
+#define PUSH (1<<6) /* PUSH onto stack */
+#define POP (2<<6) /* POP from stack */
+#define JMP (3<<6) /* 8-bit relative JMP */
/* Short forms for the table. */
#define X 0 /* invalid for some random reason */
O|M|1, O|M|4, O|M|1, O|M|1, O|M, O|M, O|M, O|M,
O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M|POP,
/* 0x90 - 0x9F */
- O, O, O, O, O, O, O, O,
+ O, O, O, O, S, O, O, O,
O, O, X, O, O, O, O, O,
/* 0xA0 - 0xAF */
O|1, O|4, O|1, O|4, O, O, O, O,
O, O, O, O, O, O, O|M, X
};
-static unsigned int get_insn_len(unsigned char *insn,
- unsigned char *p_opcode,
- unsigned char *p_decode)
+static unsigned int parse_insn(unsigned char *insn,
+ unsigned char *p_opcode,
+ unsigned char *p_decode)
{
unsigned char b, d, *pb, mod, rm;
/* 2. Ensure we have a valid opcode byte. */
if ( !(d & OPCODE_BYTE) )
- {
- printk(KERN_ALERT " *** %02x %02x %02x\n", pb[0], pb[1], pb[2]);
return 0;
- }
/* 3. Process Mod/RM if there is one. */
if ( d & HAS_MODRM )
}
}
- /* 4. All done. Result is all byte sstepped over, plus any immediates. */
+ /* 4. All done. Result is all bytes stepped over, plus any immediates. */
return ((pb - insn) + 1 + (d & INSN_SUFFIX_BYTES));
}
+/* Bitmap of faulting instructions that we can handle. */
static unsigned char handleable_code[32] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
+/* Bitmap of opcodes that use a register operand specified by Mod/RM. */
+static unsigned char opcode_uses_reg[32] = {
+ /* 0x00 - 0x3F */
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ /* 0x40 - 0x7F */
+ 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0A, 0x00, 0x00,
+ /* 0x80 - 0xBF */
+ 0xF0, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0xC0 - 0xFF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
asmlinkage void do_fixup_4gb_segment(struct pt_regs *regs, long error_code)
{
static unsigned int fixup_idx = 0;
unsigned int fi;
- int save_indirect_reg, hash;
+ int save_indirect_reg, hash, i;
unsigned int insn_len = (unsigned int)error_code, new_insn_len;
- unsigned char b[20], modrm, mod, reg, rm, patch[PATCH_LEN], opcode, decode;
+ unsigned char b[20], modrm, mod, reg, rm, sib, patch[20], opcode, decode;
unsigned long eip = regs->eip - insn_len;
struct fixup_entry *fe;
pte_t *pte;
DPRINTK("User executing out of kernel space?!");
return;
}
-
- if ( unlikely(((eip ^ (eip+PATCH_LEN)) & PAGE_MASK) != 0) )
- {
- DPRINTK("Patch instruction would straddle a page boundary.");
- return;
- }
/*
- * Check that the page to be patched is part of a read-only VMA. This
+ * Check that the page to be patched is part of a private VMA. This
* means that our patch will never erroneously get flushed to disc.
*/
if ( eip > (FIXUP_BUF_USER + FIXUP_BUF_SIZE) ) /* don't check fixup area */
{
- /* [SMP] Need to the mmap_sem semaphore. */
+ /* [SMP] Need to grab the mmap_sem semaphore. */
struct vm_area_struct *vma = find_vma(current->mm, eip);
if ( (vma == NULL) || (vma->vm_flags & VM_MAYSHARE) )
{
return;
}
- /* Already created a fixup for this address and code sequence? */
+ /* Already created a fixup for this code sequence? */
hash = FIXUP_HASH(b);
- for ( fe = fixup_hash[hash];
- fe != NULL; fe = fe->next )
+ for ( fe = fixup_hash[hash]; fe != NULL; fe = fe->next )
{
- if ( eip != fe->patch_addr )
- continue; /* XXX */
if ( memcmp(fe->patched_code, b, fe->patched_code_len) == 0 )
goto do_the_patch;
}
/* Guaranteed enough room to patch? */
- if ( unlikely((fi = fixup_idx) > (FIXUP_BUF_SIZE-32)) )
+ if ( unlikely((fi = fixup_idx) > (FIXUP_BUF_SIZE-64)) )
{
static int printed = 0;
if ( !printed )
if ( save_indirect_reg )
fixup_buf[fi++] = 0x50 + rm;
+ /* pushf */
+ fixup_buf[fi++] = 0x9c;
+
/* add %gs:0,<r32> */
fixup_buf[fi++] = 0x65;
fixup_buf[fi++] = 0x03;
*(unsigned long *)&fixup_buf[fi] = 0;
fi += 4;
+ /* popf */
+ fixup_buf[fi++] = 0x9d;
+
/* Relocate the faulting instruction, minus the GS override. */
memcpy(&fixup_buf[fi], &b[1], error_code - 1);
fi += error_code - 1;
/* Bail if can't decode the following instruction. */
if ( unlikely((new_insn_len =
- get_insn_len(&b[insn_len], &opcode, &decode)) == 0) )
+ parse_insn(&b[insn_len], &opcode, &decode)) == 0) )
{
DPRINTK("Could not decode following instruction.");
return;
if ( (decode & CODE_MASK) == JMP )
{
- return;
+ long off;
memcpy(&fixup_buf[fi], &b[insn_len], new_insn_len - 1);
fi += new_insn_len - 1;
/* Patch the 8-bit relative offset. */
fixup_buf[fi++] = 1;
-
+
insn_len += new_insn_len;
ASSERT(insn_len >= PATCH_LEN);
/* ret */
fixup_buf[fi++] = 0xc3;
- /* jmp <rel32> */
- fixup_buf[fi++] = 0xe9;
- fi += 4;
- *(unsigned long *)&fixup_buf[fi-4] =
- (eip + insn_len + (long)(char)b[insn_len-1]) -
- (FIXUP_BUF_USER + fi);
-
+ /* pushf */
+ fixup_buf[fi++] = 0x9c;
+
+ off = (insn_len - PATCH_LEN) + (long)(char)b[insn_len-1];
+ if ( unlikely(off > 127) )
+ {
+ /* add <imm32>,4(%esp) */
+ fixup_buf[fi++] = 0x81;
+ fixup_buf[fi++] = 0x44;
+ fixup_buf[fi++] = 0x24;
+ fixup_buf[fi++] = 0x04;
+ fi += 4;
+ *(long *)&fixup_buf[fi-4] = off;
+ }
+ else
+ {
+ /* add <imm8>,4(%esp) [sign-extended] */
+ fixup_buf[fi++] = 0x83;
+ fixup_buf[fi++] = 0x44;
+ fixup_buf[fi++] = 0x24;
+ fixup_buf[fi++] = 0x04;
+ fixup_buf[fi++] = (char)(off & 0xff);
+ }
+
+ /* popf */
+ fixup_buf[fi++] = 0x9d;
+
+ /* ret */
+ fixup_buf[fi++] = 0xc3;
+
break;
}
- else if ( opcode == 0xe9 )
+ else if ( opcode == 0xe9 ) /* jmp <rel32> */
{
- return;
-
- memcpy(&fixup_buf[fi], &b[insn_len], new_insn_len - 4);
- fi += new_insn_len - 4;
-
insn_len += new_insn_len;
ASSERT(insn_len >= PATCH_LEN);
- /* Patch the 32-bit relative offset. */
+ /* pushf */
+ fixup_buf[fi++] = 0x9c;
+
+ /* add <imm32>,4(%esp) */
+ fixup_buf[fi++] = 0x81;
+ fixup_buf[fi++] = 0x44;
+ fixup_buf[fi++] = 0x24;
+ fixup_buf[fi++] = 0x04;
fi += 4;
- *(unsigned long *)&fixup_buf[fi-4] =
- (eip + insn_len + *(long *)&b[insn_len-4]) -
- (FIXUP_BUF_USER + fi);
+ *(long *)&fixup_buf[fi-4] =
+ (insn_len - PATCH_LEN) + *(long *)&b[insn_len-4];
+
+ /* popf */
+ fixup_buf[fi++] = 0x9d;
/* ret */
fixup_buf[fi++] = 0xc3;
break;
}
- else if ( (decode & CODE_MASK) == PUSH )
+ else if ( opcode == 0xc3 ) /* ret */
{
- return;
- }
- else if ( (decode & CODE_MASK) == POP )
- {
- return;
+ /* pop -4(%esp) [doesn't affect EFLAGS] */
+ fixup_buf[fi++] = 0x8f;
+ fixup_buf[fi++] = 0x44;
+ fixup_buf[fi++] = 0x24;
+ fixup_buf[fi++] = 0xfc;
}
else
{
- /* Relocate the instruction verbatim. */
- memcpy(&fixup_buf[fi], &b[insn_len], new_insn_len);
- fi += new_insn_len;
+ int stack_addon = 4;
+
+ if ( (decode & CODE_MASK) == PUSH )
+ {
+ stack_addon = 8;
+ /* push (%esp) */
+ fixup_buf[fi++] = 0xff;
+ fixup_buf[fi++] = 0x34;
+ fixup_buf[fi++] = 0x24;
+ }
+ else if ( (decode & CODE_MASK) == POP )
+ {
+ stack_addon = 8;
+ /* push 4(%esp) */
+ fixup_buf[fi++] = 0xff;
+ fixup_buf[fi++] = 0x74;
+ fixup_buf[fi++] = 0x24;
+ fixup_buf[fi++] = 0x04;
+ }
+
+ /* Check for EA calculations involving ESP, and skip return addr */
+ if ( decode & HAS_MODRM )
+ {
+ do { new_insn_len--; }
+ while ( (fixup_buf[fi++] = b[insn_len++]) != opcode );
+
+ modrm = fixup_buf[fi++] = b[insn_len++];
+ new_insn_len--;
+ mod = (modrm >> 6) & 3;
+ reg = (modrm >> 3) & 7;
+ rm = (modrm >> 0) & 7;
+
+ if ( (reg == 4) &&
+ test_bit(opcode, (unsigned long *)opcode_uses_reg) )
+ {
+ DPRINTK("Data movement to ESP unsupported.");
+ return;
+ }
+
+ if ( rm == 4 )
+ {
+ if ( mod == 3 )
+ {
+ DPRINTK("Data movement to ESP is unsupported.");
+ return;
+ }
+
+ sib = fixup_buf[fi++] = b[insn_len++];
+ new_insn_len--;
+ if ( (sib & 7) == 4 )
+ {
+ switch ( mod )
+ {
+ case 0:
+ mod = 1;
+ fixup_buf[fi-2] |= 0x40;
+ fixup_buf[fi++] = stack_addon;
+ break;
+ case 1:
+ fixup_buf[fi++] = b[insn_len++] + stack_addon;
+ new_insn_len--;
+ break;
+ case 2:
+ *(long *)&fixup_buf[fi] =
+ *(long *)&b[insn_len] + stack_addon;
+ fi += 4;
+ insn_len += 4;
+ new_insn_len -= 4;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Relocate the (remainder of) the instruction. */
+ if ( new_insn_len != 0 )
+ {
+ memcpy(&fixup_buf[fi], &b[insn_len], new_insn_len);
+ fi += new_insn_len;
+ }
+
+ if ( (decode & CODE_MASK) == PUSH )
+ {
+ /* pop 4(%esp) */
+ fixup_buf[fi++] = 0x8f;
+ fixup_buf[fi++] = 0x44;
+ fixup_buf[fi++] = 0x24;
+ fixup_buf[fi++] = 0x04;
+ }
+ else if ( (decode & CODE_MASK) == POP )
+ {
+ /* pop (%esp) */
+ fixup_buf[fi++] = 0x8f;
+ fixup_buf[fi++] = 0x04;
+ fixup_buf[fi++] = 0x24;
+ }
}
if ( (insn_len += new_insn_len) > 20 )
}
/* Can't have a RET in the middle of a patch sequence. */
- if ( (opcode == 0xc4) && (insn_len < PATCH_LEN) )
+ if ( (opcode == 0xc3) && (insn_len < PATCH_LEN) )
{
DPRINTK("RET in middle of patch seq!\n");
return;
DPRINTK("Not enough memory to allocate a fixup_entry.");
return;
}
- fe->patch_addr = eip; /* XXX */
fe->patched_code_len = insn_len;
memcpy(fe->patched_code, b, insn_len);
fe->fixup_idx = fixup_idx;
fe->return_idx =
- fixup_idx + error_code + 6 + 4/**/ + (save_indirect_reg ? 2 : 0);
+ fixup_idx + error_code + (save_indirect_reg ? 14 : 12);
fe->next = fixup_hash[hash];
fixup_hash[hash] = fe;
fixup_idx = fi;
do_the_patch:
-#if 1
- /* Create the patching instruction in a temporary buffer. */
+
+ if ( unlikely(((eip ^ (eip + fe->patched_code_len)) & PAGE_MASK) != 0) )
+ {
+ DPRINTK("Patch instruction would straddle a page boundary.");
+ return;
+ }
+
+ /* Create the patching instructions in a temporary buffer. */
patch[0] = 0x67;
patch[1] = 0xff;
patch[2] = 0x16; /* call <r/m16> */
*(u16 *)&patch[3] = FIXUP_BUF_USER + fe->fixup_idx;
-#else
- patch[0] = 0x9a;
- *(u32 *)&patch[1] = FIXUP_BUF_USER + fe->fixup_idx + 4;
- *(u16 *)&patch[5] = __USER_CS;
-#endif
-
-#if 1
- {
- int iii;
- printk(KERN_ALERT "EIP == %08lx; USER_EIP == %08lx\n",
- eip, FIXUP_BUF_USER + fe->fixup_idx);
- printk(KERN_ALERT " .byte ");
- for ( iii = 0; iii < insn_len; iii++ )
- printk("0x%02x,", b[iii]);
- printk("!!\n");
- printk(KERN_ALERT " .byte ");
- for ( iii = fe->fixup_idx; iii < fi; iii++ )
- printk("0x%02x,", (unsigned char)fixup_buf[iii]);
- printk("!!\n");
- printk(KERN_ALERT " .byte ");
- for ( iii = 0; iii < 7; iii++ )
- printk("0x%02x,", (unsigned char)patch[iii]);
- printk("!!\n");
- }
-#endif
+ for ( i = 5; i < fe->patched_code_len; i++ )
+ patch[i] = 0x90; /* nop */
- if ( put_user(eip + insn_len, (unsigned long *)regs->esp - 1) != 0 )
+ if ( put_user(eip + PATCH_LEN, (unsigned long *)regs->esp - 1) != 0 )
{
DPRINTK("Failed to place return address on user stack.");
return;
pmd = pmd_offset(pgd, eip);
pte = pte_offset_kernel(pmd, eip);
veip = kmap(pte_page(*pte));
- memcpy((char *)veip + (eip & ~PAGE_MASK), patch, PATCH_LEN);
+ memcpy((char *)veip + (eip & ~PAGE_MASK), patch, fe->patched_code_len);
kunmap(pte_page(*pte));
return;
struct page *_pages[1<<FIXUP_BUF_ORDER], **pages=_pages;
int i;
- /*if ( nosegfixup )*/
+ if ( nosegfixup )
return 0;
HYPERVISOR_vm_assist(VMASST_CMD_enable,